#!/bin/bash

get_args_length() {
  local apps=($@)
  local length=${#apps[@]}

  echo $length
}

string::split() {
  local seperator=$1
  local str=$2

  if [ "$seperator" = "" ] || [ "$str" = "" ]; then
    log::error "seperator(\$1) and str(\$2) is required"
    exit 1
  fi

  if [ "$seperator" = "<>" ]; then
    seperator=" "
  fi

  echo $str | awk -F $seperator '{out=""; for(i=1;i<=NF;i++){out=out" "$i}; print out}'
}

string::split_length() {
  local seperator=$1
  local str=$2

  if [ "$seperator" = "" ] || [ "$str" = "" ]; then
    log::error "seperator(\$1) and str(\$2) is required"
    exit 1
  fi

  echo $str | awk -F ${seperator} '{print NF}'
}

string::split_last_element() {
  local seperator=$1
  local str=$2

  if [ "$seperator" = "" ] || [ "$str" = "" ]; then
    log::error "seperator(\$1) and str(\$2) is required"
    exit 1
  fi

  echo $str | awk -F $seperator '{print $(NF-0)}'
}

string::split_last_second_element() {
  local seperator=$1
  local str=$2

  if [ "$seperator" = "" ] || [ "$str" = "" ]; then
    log::error "seperator(\$1) and str(\$2) is required"
    exit 1
  fi

  echo $str | awk -F $seperator '{print $(NF-1)}'
}

string_split_slice() {
  local seperator=$1
  local str=$2
  local start=$3
  local end=$4

  if [ "$seperator" = "" ] || [ "$str" = "" ] || [ "$start" = "" ]; then
    log::error "seperator, str, start is required"
    exit 1
  fi

  local array=$(string::split $seperator $str)
  local length=$(get_args_length $array)

  # not set, use default length
  end=${end:-$length}
  # -1, use default length
  if [ $end -eq -1 ]; then
    end=$((length - 1))
  fi

  local res=""
  local index=0
  for element in $array; do
    # echo "$index $element $start $end"
    # >= start && < end
    if [ $index -ge $start ] && [ $index -lt $end ]; then
      res="$res $element"
      # echo "$res"
      index=$(number::step $index)
      continue
    fi

    if [ $index -gt $end ]; then
      break
    fi

    index=$(number::step $index)
  done

  # echo x:$res
  echo $(array::join $seperator $res)
}

string::replace() {
  local base=$1
  local origin=$2
  local new=$3

  if [ "$new" = "<>" ] || [ "$new" = "" ]; then
    new=""
  fi

  if [ "$base" = "" ] || [ "$origin" = "" ]; then
    log::error "base, origin, new is required"
    exit 1
  fi

  if [ "$origin" = "." ]; then
    origin="\."
  fi

  if [ "$new" = "." ]; then
    new="\."
  fi

  # @TODO
  echo $base | sed -e "s%${origin}%${new}%g"
}

string::remove() {
  local text=$1
  local need_remove=$2
  string::replace $text $need_remove
}

string::match() {
  local text=$1
  local regexp=$2

  echo $text | grep "$regexp" >>/dev/null 2>&1
  if [ "$?" = "0" ]; then
    echo "true"
    return
  fi

  echo "false"
}

number::increase() {
  local base=$1
  local _step=$2
  local step=${_step:-1}

  if [ "$base" = "" ]; then
    log::error "base is required"
    exit 1
  fi

  echo $((base + step))
}

number::step() {
  echo $(number::increase $1 $2)
}

number::add() {
  local res=0

  for num in $@; do
    res=$((res + num))
  done

  echo $res
}

array::join() {
  local seperator=$1
  local args=${@:2}

  if [ "$seperator" = "" ] || [ "$args" = "" ]; then
    log::error "seperator, args is required"
    exit 1
  fi

  if [ "$seperator" = "<>" ]; then
    seperator=""
  fi

  local res=""
  for element in $args; do
    if [ "$res" = "" ]; then
      res=$element
      continue
    fi

    res="${res}${seperator}${element}"
  done

  echo $res
}

array::join_line() {
  local seperator="\n"
  local args=${@}

  local res=""
  for element in $args; do
    if [ "$res" = "" ]; then
      res=$element
      continue
    fi

    res="${res}${seperator}${element}"
  done

  echo $res
}

array::each() {
  local fn=$1
  local args=${@:2}

  # @TODO GLOBAL, make outsize can get
  Z_ARRAY_INDEX=0
  local index=0
  for element in ${args[@]}; do
    Z_ARRAY_INDEX=$index
    $fn $index $element

    index=$((index + 1))
  done
}

array::filter() {
  local fn=$1
  local args=${@:2}
  local store=""

  # @TODO GLOBAL, make outsize can get
  Z_ARRAY_INDEX=0
  local index=0
  for element in ${args[@]}; do
    Z_ARRAY_INDEX=$index
    if [ "$($fn $element $index)" = "true" ]; then
      store="$store $element"
    fi

    index=$((index + 1))
  done

  echo $store
}

array::length() {
  echo ${#@}
}

array::find() {
  local fn=$1
  local args=${@:2}
  local store=""

  local index=0
  for element in ${args[@]}; do
    if [ "$($fn $element $index)" = "true" ]; then
      echo $element
      return
    fi

    index=$((index + 1))
  done
}

# @TODO https://unix.stackexchange.com/questions/282557/scope-of-local-variables-in-shell-functions
# same variables will be override if you use it in others functions
array::find_by_index() {
  local nth=$1
  local elements_array_find_by_index=${@:2}
  local element_array_find_by_index=""

  each() {
    if [ -n "$element_array_find_by_index" ]; then
      return
    fi

    local index=$1
    local item=$2

    if [ $index -eq $nth ]; then
      element_array_find_by_index=$item
    fi
  }

  array::each each $elements_array_find_by_index

  echo $element_array_find_by_index
}

# @TODO https://stackoverflow.com/questions/15685736/how-to-extract-a-particular-element-from-an-array-in-bash
array::nth_element() {
  array::find_by_index $@
}

array::first_element() {
  array::find_by_index 0 $@
}

array::last_element() {
  local length=$(array::length $@)
  array::find_by_index $(($length - 1)) $@
}

array::slice() {
  local start=$1
  local end=$2
  local arr=${@:3}
  local _arr=()

  slice() {
    local index=$1
    local value=$2
    if (( index < start )) || (( index >= end )); then
      return
    fi

    _arr+=("$value")
  }

  array::each slice $arr

  echo ${_arr[@]}
}

os::set_var() {
  local name=$1
  local value=$2

  eval "$name=$value"
}

export -f get_args_length

export -f string::split
export -f string::split_length
export -f string::split_last_element
export -f string::split_last_second_element
export -f string::replace
export -f string::remove
export -f string::match

export -f number::increase
export -f number::step
export -f number::add

export -f array::join
export -f array::each
export -f array::length
export -f array::filter
export -f array::find
export -f array::find_by_index
export -f array::nth_element
export -f array::first_element
export -f array::last_element
export -f array::slice

export -f array::join_line

export -f os::set_var
